﻿using System;
using System.Linq;
using System.Diagnostics;
using NLite.Collections;
using NLite.Reflection;
using System.Collections.Generic;
using NLite.Mini.Fluent;


namespace NLite.Internal
{
    [Serializable]
    class ComponentExpression : IBindingExpression, INamedExpression, IContractExpression, IComponentExpression, IFactoryExpression, IComponentInfo
    {
        public string Id { get; internal set; }
        public Type[] Contracts { get { return contracts.ToArray(); } }
        public Type Implementation { get; private set; }

        public string Activator { get; set; }
        public LifestyleFlags Lifestyle { get; set; }
        public IPropertySet ExtendedProperties
        {
            get
            {
                if (!isPopulateMetadata && Implementation != null)
                {
                    lock (extendedProperties)
                    {
                        if (!isPopulateMetadata)
                        {
                            var mappings = Implementation.GetMappings();
                            extendedProperties.AddRange(mappings);
                            isPopulateMetadata = true;
                        }
                    }
                }

                return extendedProperties;
            }
        }
        public Func<object> Factory { get; private set; }

        public IServiceRegistry Registry { get; set; }

        private HashSet<Type> contracts;

        private volatile bool isPopulateMetadata = false;
        private IPropertySet extendedProperties = new PropertySet(StringComparer.OrdinalIgnoreCase);

        public ComponentExpression()
        {
            Activator = ActivatorType.Default;
            Lifestyle = LifestyleType.Default;
            contracts = new HashSet<Type>();
        }



        public INamedExpression Named(string id)
        {
            if (string.IsNullOrEmpty(id))
                throw new ArgumentNullException("id == null");
            Id = id;

            return this;
        }


        #region INamedExpression Members

        public IContractExpression Bind(Type serive)
        {
            contracts.Add(serive);
            ContractService.GetContracts(serive,contracts);
            return this;
        }

        public IContractExpression Bind<TService>()
        {
            return Bind(typeof(TService));
        }


        public IContractExpression Bind(Type serive, string id)
        {
            contracts.Add(serive);
            ContractService.GetContracts(serive, contracts);
            return Named(id);
        }

        public IContractExpression Bind<TService>(string id)
        {
            return Bind(typeof(TService), id);
        }

        #endregion

        #region IServiceExpression Members

        public IComponentExpression To(Type type)
        {
            Trace.Assert(type != null, "implemention == null");
            Implementation = type;
            ContractService.GetContracts(type, contracts);
            if (contracts.Count == 0)
                contracts.Add(type);
            return this;
        }

        public IComponentExpression To<TImplementation>()
        {

            return To(typeof(TImplementation));
        }

        IFactoryExpression IContractExpression.Factory(Func<object> factory)
        {
            Trace.Assert(factory != null, "factory == null");
            Factory = factory;
            Activator = ActivatorType.Factory;
            return this;
        }

        #endregion

        #region IImplementationExpression Members
        IComponentExpression IComponentExpression.Lifestyle(LifestyleFlags lifestyle)
        {
            Lifestyle = lifestyle;
            return this;
        }

        IComponentExpression IComponentExpression.Activator(string activator)
        {
            Activator = activator;
            return this;
        }

        public IComponentExpression Add(string key, object value)
        {
            ExtendedProperties[key] = value;
            return this;
        }

        #endregion

        

        public override bool Equals(object obj)
        {
            return Equals((IComponentInfo)obj);
        }

        public bool Equals(IComponentInfo other)
        {
            if (other == null)
                return false;
            if (Implementation != other.Implementation)
                return false;
            if (this.Factory != other.Factory)
                return false;
            if (this.Activator != other.Activator)
                return false;
            return true;
        }


        #region IFactoryExpression Members

      
        IFactoryExpression IFactoryExpression.Lifestyle(LifestyleFlags lifestyle)
        {
            Lifestyle = lifestyle;
            return this;
        }



        IFactoryExpression IFactoryExpression.Add(string key, object value)
        {
            ExtendedProperties[key]= value;
            return this;
        }

        #endregion
    }

}
